/*
 * Reksio - Memory Map Editor
 * Copyright (C) 2023 CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * In applying this licence, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an Intergovernmental Organization or
 * submit itself to any jurisdiction.
 */

#ifndef ATTRIBUTECONTAINER_H
#define ATTRIBUTECONTAINER_H
#include <memory>
#include <vector>
#include <string>

class Attribute; //fwd decl
class MemoryNode; // fwd decl
class AttributeContainerValidator; // fwd decl
class AttributeValidator; // fwd decl
struct INodeRuleValidator; // fwd decl

class AttributeContainer
{
    using attributes_type  = std::vector<std::unique_ptr<Attribute>>;
    using containers_type  = std::vector<std::unique_ptr<AttributeContainer>>;
public:
    explicit AttributeContainer(const std::string& name);
    AttributeContainer(const AttributeContainer& rhs);
    AttributeContainer& operator=(const AttributeContainer& rhs);
    AttributeContainer& operator=(AttributeContainer&& rhs);
    AttributeContainer(AttributeContainer&& rhs);

    const std::string& getName() const;
    const attributes_type& getAttributes() const;
    const containers_type& getContainers() const;

    int getAttributesSize() const;
    int getContainersSize() const;
    int getTotalSize() const;
    // get attributes
    Attribute* getAttribute(const int index) const;
    Attribute* getAttribute(const std::vector<std::string>& name) const;
    Attribute* getAttribute(const std::string& name) const;
    std::vector<Attribute*> getAllAttributes() const;
    std::vector<AttributeContainer*> getAllAttributeContainers() const;
    // get container
    AttributeContainer* getAttributeContainer(const int index) const;
    AttributeContainer* getAttributeContainer(const std::string& name) const;
    AttributeContainer* getAttributeContainer(const std::vector<std::string>& name) const;


    // add attributes
    void addAttribute(std::unique_ptr<Attribute> attr);
    void addAttribute(Attribute* attr);

    // remove attributes
    bool removeAttribute(const std::string& name);
    bool removeAttribute(Attribute& attr);
    bool removeAttribute(const int& position);
    std::unique_ptr<Attribute> releaseAttribute(const int position);

    // find attribute
    attributes_type::const_iterator findAttribute(const std::string& name) const;
    attributes_type::iterator findAttribute(const std::string& name);
    bool hasAttribute(const std::string& name) const;
    bool hasAttribute(const std::vector<std::string>& name) const;

    // misc.
    int attributeIndex(const std::string& attribute_name) const;
    int containerIndex() const;
    int containerIndex(const std::string& container_name) const;
    void setName(const std::string& new_name);
    std::vector<std::string> getFullName() const;
    bool isValid() const;

    // add container
    void addContainer(std::unique_ptr<AttributeContainer> attr_container);
    void addContainer(AttributeContainer* attr_container);
    void addContainer(const std::string& name);
    void addContainers(const std::vector<std::string> names);

    // remove containers
    bool removeAttributeContainer(const int& position);
    std::unique_ptr<AttributeContainer> releaseAttributeContainer(const int position);

    // find containers
    bool hasContainer(const std::string& name) const;

    // validator
    AttributeContainerValidator* getValidator() const;
    const std::vector<std::pair<Attribute*, std::vector<INodeRuleValidator*>>> getFailedValidators() const;
    std::vector<AttributeValidator*> getMissingAttributes() const;

    // parents
    bool isRoot() const;
    AttributeContainer* getRoot();
    const AttributeContainer* getRoot() const;
    void setParent(MemoryNode* node);
    void setParent(AttributeContainer* attr_container);
    AttributeContainer* getParent() const;
    MemoryNode* getParentNode() const;

    bool isDeprecated() const;

    bool hasDeprecated() const;

    std::vector<Attribute*> getDeprecatedAttributes() const;
    std::vector<AttributeContainer*> getDeprecatedAttributeContainers() const;


protected:
    std::string name_;
    attributes_type attributes_;
    containers_type containers_;
    AttributeContainer* parent_ = nullptr;
    MemoryNode* parent_node_ = nullptr;
protected:
    void updateParent();
    void copy(const AttributeContainer& rhs);
};

std::ostream& operator<<(std::ostream& stream, const AttributeContainer& attribute_container);

#endif // ATTRIBUTECONTAINER_H
